"""PostGIS空间数据可视化服务

使用PyDeck实现空间数据的Web地图可视化。
"""

import streamlit as st
import pydeck as pdk
import pandas as pd
from typing import List, Dict, Any, Optional
import json


class SpatialVisualization:
    """空间数据可视化类"""
    
    def __init__(self):
        """初始化可视化组件"""
        # 默认地图视角设置 - 调整为能显示所有省份的视角
        self.default_view_state = pdk.ViewState(
            latitude=32.0,  # 中国中部，能覆盖广东到辽宁的范围
            longitude=116.0,
            zoom=4,  # 降低缩放级别以显示更大范围
            pitch=0
        )
    
    def _extract_outer_ring_coordinates(self, geometry: Dict[str, Any]) -> List[List[float]]:
        """提取几何体的外环坐标
        
        Args:
            geometry: GeoJSON几何对象
            
        Returns:
            外环坐标列表 [[lon, lat], ...]
        """
        if geometry['type'] == 'MultiPolygon':
            # MultiPolygon: 选择点数最多的Polygon的外环
            max_polygon = None
            max_points = 0
            
            for polygon in geometry['coordinates']:
                # polygon[0]是外环坐标
                point_count = len(polygon[0])
                if point_count > max_points:
                    max_points = point_count
                    max_polygon = polygon[0]
            
            return max_polygon if max_polygon is not None else geometry['coordinates'][0][0]
        elif geometry['type'] == 'Polygon':
            # Polygon: coordinates[0]是外环
            return geometry['coordinates'][0]
        else:
            raise ValueError(f"不支持的几何类型: {geometry['type']}")
    
    def _calculate_geometry_center(self, geometry: Dict[str, Any]) -> tuple[float, float]:
        """计算几何体的中心点
        
        Args:
            geometry: GeoJSON几何对象
            
        Returns:
            (latitude, longitude) 中心点坐标
        """
        coords = self._extract_outer_ring_coordinates(geometry)
        center_lat = sum(coord[1] for coord in coords) / len(coords)
        center_lon = sum(coord[0] for coord in coords) / len(coords)
        return center_lat, center_lon
    
    def create_points_layer(self, points_data: List[Dict[str, Any]], 
                           color: List[int] = [255, 0, 0, 160]) -> pdk.Layer:
        """创建点要素图层
        
        Args:
            points_data: 点要素数据列表
            color: 点的颜色 [R, G, B, A]
            
        Returns:
            PyDeck点图层
        """
        if not points_data:
            return None
            
        # 转换数据格式
        df_points = []
        for point in points_data:
            coords = point['geometry']['coordinates']
            df_points.append({
                'id': point['id'],
                'name': point['name'],
                'description': point.get('description', ''),
                'category': point.get('category', ''),
                'longitude': coords[0],
                'latitude': coords[1]
            })
        
        df = pd.DataFrame(df_points)
        
        return pdk.Layer(
            'ScatterplotLayer',
            data=df,
            get_position='[longitude, latitude]',
            get_color=color,
            get_radius=1000,
            radius_scale=1,
            radius_min_pixels=5,
            radius_max_pixels=15,
            pickable=True,
            auto_highlight=True
        )
    
    def create_polygons_layer(self, polygons_data: List[Dict[str, Any]], 
                             fill_color: List[int] = [0, 255, 0, 100],
                             line_color: List[int] = [0, 255, 0, 255]) -> pdk.Layer:
        """创建面要素图层
        
        Args:
            polygons_data: 面要素数据列表
            fill_color: 填充颜色 [R, G, B, A]
            line_color: 边界线颜色 [R, G, B, A]
            
        Returns:
            PyDeck面图层
        """
        if not polygons_data:
            return None
            
        # 转换数据格式
        df_polygons = []
        for polygon in polygons_data:
            try:
                coords = self._extract_outer_ring_coordinates(polygon['geometry'])
                df_polygons.append({
                    'id': polygon['id'],
                    'name': polygon['name'],
                    'description': polygon.get('description', ''),
                    'category': polygon.get('category', ''),
                    'area': polygon.get('area', 0),
                    'coordinates': coords
                })
            except ValueError as e:
                print(f"警告: 跳过面要素 {polygon['name']}: {e}")
                continue
        
        if not df_polygons:
            return None
            
        df = pd.DataFrame(df_polygons)
        
        return pdk.Layer(
            'PolygonLayer',
            data=df,
            get_polygon='coordinates',
            get_fill_color=fill_color,
            get_line_color=line_color,
            get_line_width=2,
            pickable=True,
            auto_highlight=True
        )
    
    def create_regions_layer(self, regions_data: List[Dict[str, Any]], 
                            fill_color: List[int] = [0, 0, 255, 80],
                            line_color: List[int] = [0, 0, 255, 255]) -> pdk.Layer:
        """创建区域图层
        
        Args:
            regions_data: 区域数据列表
            fill_color: 填充颜色 [R, G, B, A]
            line_color: 边界线颜色 [R, G, B, A]
            
        Returns:
            PyDeck区域图层
        """
        if not regions_data:
            return None
            
        # 转换数据格式
        df_regions = []
        for region in regions_data:
            try:
                coords = self._extract_outer_ring_coordinates(region['geometry'])
                df_regions.append({
                    'id': region['id'],
                    'name': region['name'],
                    'description': region.get('description', ''),
                    'region_type': region.get('region_type', ''),
                    'level': region.get('level', 0),
                    'coordinates': coords
                })
            except ValueError as e:
                print(f"警告: 跳过区域 {region['name']}: {e}")
                continue
        
        if not df_regions:
            return None
            
        df = pd.DataFrame(df_regions)
        
        return pdk.Layer(
            'PolygonLayer',
            data=df,
            get_polygon='coordinates',
            get_fill_color=fill_color,
            get_line_color=line_color,
            get_line_width=3,
            pickable=True,
            auto_highlight=True
        )
    
    def render_points_in_polygon_demo(self, polygon_data: Dict[str, Any], 
                                     points_data: List[Dict[str, Any]]) -> None:
        """渲染点在面内关系演示
        
        Args:
            polygon_data: 面要素数据
            points_data: 包含在面内的点要素数据列表
        """
        st.subheader("🎯 点在面内空间关系演示")
        
        if not polygon_data:
            st.warning("请先选择一个面要素")
            return
        
        # 显示选中的面要素信息
        st.info(f"**选中面要素**: {polygon_data['name']} ({polygon_data.get('category', '未分类')})")
        
        if points_data:
            st.success(f"找到 {len(points_data)} 个包含在该面要素内的点要素")
            
            # 创建图层
            polygon_layer = self.create_polygons_layer(
                [polygon_data], 
                fill_color=[255, 165, 0, 100],  # 橙色填充
                line_color=[255, 165, 0, 255]   # 橙色边界
            )
            
            points_layer = self.create_points_layer(
                points_data,
                color=[255, 0, 0, 200]  # 红色点
            )
            
            # 计算地图中心
            center_lat, center_lon = self._calculate_geometry_center(polygon_data['geometry'])
            
            # 渲染地图
            deck = pdk.Deck(
                layers=[polygon_layer, points_layer],
                initial_view_state=pdk.ViewState(
                    latitude=center_lat,
                    longitude=center_lon,
                    zoom=10,
                    pitch=0
                ),
                tooltip={
                    'html': '<b>{name}</b><br/>类别: {category}<br/>描述: {description}',
                    'style': {'backgroundColor': 'steelblue', 'color': 'white'}
                }
            )
            
            st.pydeck_chart(deck)
            
            # 显示点要素列表
            with st.expander("📍 包含的点要素详情"):
                for point in points_data:
                    st.write(f"• **{point['name']}** ({point.get('category', '未分类')})")
                    if point.get('description'):
                        st.write(f"  {point['description']}")
        else:
            st.warning("该面要素内没有包含任何点要素")
    

    
    def render_overview_map(self, points_data: List[Dict[str, Any]], 
                           polygons_data: List[Dict[str, Any]], 
                           regions_data: List[Dict[str, Any]]) -> None:
        """渲染总览地图
        
        Args:
            points_data: 所有点要素数据
            polygons_data: 所有面要素数据
            regions_data: 所有区域数据
        """
        st.subheader("🗺️ 空间数据总览")
        
        layers = []
        
        # 添加区域图层（最底层）
        if regions_data:
            regions_layer = self.create_regions_layer(
                regions_data,
                fill_color=[0, 0, 255, 40],
                line_color=[0, 0, 255, 200]
            )
            if regions_layer is not None:
                layers.append(regions_layer)
        
        # 添加面要素图层（中间层）
        if polygons_data:
            polygons_layer = self.create_polygons_layer(
                polygons_data,
                fill_color=[0, 255, 0, 80],
                line_color=[0, 255, 0, 200]
            )
            if polygons_layer is not None:
                layers.append(polygons_layer)
        
        # 添加点要素图层（最上层）
        if points_data:
            points_layer = self.create_points_layer(
                points_data,
                color=[255, 0, 0, 180]
            )
            if points_layer is not None:
                layers.append(points_layer)
        
        if layers:
            # 渲染地图
            deck = pdk.Deck(
                layers=layers,
                initial_view_state=self.default_view_state,
                tooltip={
                    'html': '<b>{name}</b><br/>类别: {category}<br/>描述: {description}',
                    'style': {'backgroundColor': 'steelblue', 'color': 'white'}
                }
            )
            
            st.pydeck_chart(deck)
            
            # 显示图例
            col1, col2, col3 = st.columns(3)
            with col1:
                st.markdown("🔵 **区域** (蓝色)")
                if regions_data:
                    st.write(f"共 {len(regions_data)} 个区域")
            
            with col2:
                st.markdown("🟢 **面要素** (绿色)")
                if polygons_data:
                    st.write(f"共 {len(polygons_data)} 个面要素")
            
            with col3:
                st.markdown("🔴 **点要素** (红色)")
                if points_data:
                    st.write(f"共 {len(points_data)} 个点要素")
        else:
            st.warning("没有可显示的空间数据")
    
    def create_spatial_relationship_map(self, map_data: Dict[str, Any]) -> pdk.Deck:
        """创建空间关系地图
        
        Args:
            map_data: 包含points和polygons的地图数据
            
        Returns:
            PyDeck地图对象
        """
        layers = []
        
        # 添加面要素图层
        if map_data.get('polygons'):
            polygons_layer = self.create_polygons_layer(
                map_data['polygons'],
                fill_color=[0, 255, 0, 80],
                line_color=[0, 255, 0, 200]
            )
            layers.append(polygons_layer)
        
        # 添加点要素图层
        if map_data.get('points'):
            points_layer = self.create_points_layer(
                map_data['points'],
                color=[255, 0, 0, 180]
            )
            layers.append(points_layer)
        
        # 计算地图中心
        center_lat, center_lon = 39.9042, 116.4074  # 默认北京
        zoom_level = 10
        
        if map_data.get('polygons'):
            # 使用第一个面要素计算中心
            polygon = map_data['polygons'][0]
            center_lat, center_lon = self._calculate_geometry_center(polygon['geometry'])
        elif map_data.get('points'):
            # 使用第一个点要素计算中心
            point = map_data['points'][0]
            coords = point['geometry']['coordinates']
            center_lat = coords[1]
            center_lon = coords[0]
        
        return pdk.Deck(
            layers=layers,
            initial_view_state=pdk.ViewState(
                latitude=center_lat,
                longitude=center_lon,
                zoom=zoom_level,
                pitch=0
            ),
            tooltip={
                'html': '<b>{name}</b><br/>类别: {category}<br/>描述: {description}',
                'style': {'backgroundColor': 'steelblue', 'color': 'white'}
            }
        )
    
    def render_region_gis_analysis(self, region_data: Dict[str, Any]) -> None:
        """渲染区域GIS分析演示
        
        Args:
            region_data: 包含GIS分析信息的区域数据
        """
        st.subheader("📊 区域GIS分析")
        
        if not region_data:
            st.warning("请先选择一个区域")
            return
        
        # 显示区域基本信息
        st.info(f"**分析区域**: {region_data['name']} ({region_data.get('region_type', '未分类')})")
        
        gis_metrics = region_data.get('gis_metrics', {})
        
        # 显示GIS指标
        col1, col2, col3, col4 = st.columns(4)
        
        with col1:
            if gis_metrics.get('area_sqkm'):
                st.metric(
                    "面积", 
                    f"{gis_metrics['area_sqkm']:,.2f} km²",
                    help=f"精确面积: {gis_metrics.get('area_sqm', 0):,.2f} m²"
                )
            else:
                st.metric("面积", "计算中...")
        
        with col2:
            if gis_metrics.get('perimeter_km'):
                st.metric(
                    "周长", 
                    f"{gis_metrics['perimeter_km']:,.2f} km",
                    help=f"精确周长: {gis_metrics.get('perimeter_m', 0):,.2f} m"
                )
            else:
                st.metric("周长", "计算中...")
        
        with col3:
            if gis_metrics.get('centroid_lat') and gis_metrics.get('centroid_lon'):
                st.metric(
                    "中心点纬度", 
                    f"{gis_metrics['centroid_lat']:.6f}°"
                )
            else:
                st.metric("中心点纬度", "计算中...")
        
        with col4:
            if gis_metrics.get('centroid_lat') and gis_metrics.get('centroid_lon'):
                st.metric(
                    "中心点经度", 
                    f"{gis_metrics['centroid_lon']:.6f}°"
                )
            else:
                st.metric("中心点经度", "计算中...")
        
        # 创建地图可视化
        if gis_metrics.get('centroid_lat') and gis_metrics.get('centroid_lon'):
            # 创建区域图层
            region_layer = self.create_regions_layer(
                [region_data],
                fill_color=[0, 150, 255, 100],  # 蓝色填充
                line_color=[0, 100, 200, 255]   # 深蓝色边界
            )
            
            # 创建中心点图层
            centroid_data = [{
                'id': f"centroid_{region_data['id']}",
                'name': f"{region_data['name']}中心点",
                'category': '几何中心',
                'description': f"纬度: {gis_metrics['centroid_lat']:.6f}, 经度: {gis_metrics['centroid_lon']:.6f}",
                'geometry': {
                    'type': 'Point',
                    'coordinates': [gis_metrics['centroid_lon'], gis_metrics['centroid_lat']]
                }
            }]
            
            centroid_layer = self.create_points_layer(
                centroid_data,
                color=[255, 0, 0, 200]  # 红色中心点
            )
            
            layers = []
            if region_layer:
                layers.append(region_layer)
            if centroid_layer:
                layers.append(centroid_layer)
            
            # 渲染地图
            if layers:
                deck = pdk.Deck(
                    layers=layers,
                    initial_view_state=pdk.ViewState(
                        latitude=gis_metrics['centroid_lat'],
                        longitude=gis_metrics['centroid_lon'],
                        zoom=7,
                        pitch=0
                    ),
                    tooltip={
                        'html': '<b>{name}</b><br/>类别: {category}<br/>描述: {description}',
                        'style': {'backgroundColor': 'steelblue', 'color': 'white'}
                    }
                )
                
                st.pydeck_chart(deck)
        
        # 显示详细GIS分析信息
        with st.expander("🔍 详细GIS分析信息"):
            st.markdown("### 📐 几何属性")
            
            if gis_metrics:
                col1, col2 = st.columns(2)
                
                with col1:
                    st.markdown("**面积信息:**")
                    st.write(f"• 平方米: {gis_metrics.get('area_sqm', 0):,.2f} m²")
                    st.write(f"• 平方公里: {gis_metrics.get('area_sqkm', 0):,.2f} km²")
                    st.write(f"• 公顷: {gis_metrics.get('area_sqm', 0) / 10000:,.2f} ha")
                    
                    st.markdown("**周长信息:**")
                    st.write(f"• 米: {gis_metrics.get('perimeter_m', 0):,.2f} m")
                    st.write(f"• 公里: {gis_metrics.get('perimeter_km', 0):,.2f} km")
                
                with col2:
                    st.markdown("**中心点坐标:**")
                    if gis_metrics.get('centroid_lat') and gis_metrics.get('centroid_lon'):
                        st.write(f"• 纬度: {gis_metrics['centroid_lat']:.6f}°")
                        st.write(f"• 经度: {gis_metrics['centroid_lon']:.6f}°")
                        st.write(f"• 坐标系: WGS84 (EPSG:4326)")
                    
                    st.markdown("**形状特征:**")
                    if gis_metrics.get('area_sqkm') and gis_metrics.get('perimeter_km'):
                        # 计算形状指数（周长与等面积圆周长的比值）
                        area_km2 = gis_metrics['area_sqkm']
                        perimeter_km = gis_metrics['perimeter_km']
                        if area_km2 > 0:
                            circle_perimeter = 2 * 3.14159 * (area_km2 / 3.14159) ** 0.5
                            shape_index = perimeter_km / circle_perimeter
                            st.write(f"• 形状指数: {shape_index:.2f}")
                            st.write(f"• 紧凑度: {1/shape_index:.2f}" if shape_index > 0 else "• 紧凑度: N/A")
            
            st.markdown("### 🗺️ PostGIS函数说明")
            st.markdown("""
            本分析使用了以下PostGIS空间分析函数：
            - **ST_Area(geography)**: 计算地理坐标系下的精确面积（平方米）
            - **ST_Perimeter(geography)**: 计算地理坐标系下的精确周长（米）
            - **ST_Centroid(geometry)**: 计算几何体的质心坐标
            - **ST_X() / ST_Y()**: 提取点坐标的X（经度）和Y（纬度）值
            
            **注意**: 使用geography类型可以获得基于地球椭球面的精确测量结果。
            """)